<?php
namespace  AdaPay\Kernel\Support;

use AdaPay\Kernel\Exceptions\DecryptException;

/**
 * 提供RSA加密解密相关功能
 */
class RSA
{
    /**
     * 生成指定长度的随机字符串
     *
     * @param int $size 字符串长度
     * @return string 生成的随机字符串
     */
    public static function aes_generate($size)
    {
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $arr = array();
        for ($i = 0; $i < $size; $i++) {
            $arr[] = $str[mt_rand(0, 61)];
        }

        return implode('', $arr);
    }

    /**
     * 使用AES加密数据
     *
     * @param array $plainText 明文数据，一般是关联数组
     * @param string $key 加密密钥
     * @return string 加密后的数据，使用base64编码
     * @throws DecryptException 如果加密失败
     */
    public static function AESEncrypt($plainText, $key)
    {
        ksort($plainText);
        $plainText = json_encode($plainText, JSON_UNESCAPED_UNICODE);
        $ivlen = openssl_cipher_iv_length($cipher = "AES-128-ECB");
        $iv = !$ivlen ? "" : openssl_random_pseudo_bytes($ivlen);
        $result = openssl_encrypt($plainText, 'AES-128-ECB', $key, OPENSSL_RAW_DATA, $iv);
        if (!$result) {
            throw new DecryptException('报文加密错误');
        }
        return base64_encode($result);
    }

    /**
     * 使用AES解密数据
     *
     * @param string $cipherText 已加密的数据，使用base64编码
     * @param string $key 解密密钥
     * @return string 解密后的数据
     * @throws DecryptException 如果解密失败
     */
    public static function AESDecrypt($cipherText, $key)
    {
        $result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);

        if (!$result) {
            throw new DecryptException('报文解密错误', 2003);
        }

        return $result;
    }

    /**
     * 使用公钥对数据进行RSA加密
     *
     * @param string $plainText 明文数据
     * @param string $puk 公钥
     * @return string 加密后的数据，使用base64编码
     * @throws DecryptException 如果加密失败
     */
    public static function RSAEncryptByPub($plainText, $puk)
    {
        if (!openssl_public_encrypt($plainText, $cipherText, $puk, OPENSSL_PKCS1_PADDING)) {
            throw new DecryptException('AESKey 加密错误');
        }

        return base64_encode($cipherText);
    }

    /**
     * 验证签名是否有效
     *
     * @param string $plainText 原始数据
     * @param string $sign 签名
     * @param string $path 公钥文件路径
     * @return int 验签结果，1表示验签成功，0表示验签失败
     * @throws DecryptException 如果验签失败
     */
    public static function verify($plainText, $sign, $path)
    {
        $resource = openssl_pkey_get_public($path);
        $result = openssl_verify($plainText, base64_decode($sign), $resource, 'SHA256');
        openssl_free_key($resource);

        if (!$result) {
            throw new DecryptException('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign, '02002');
        }

        return $result;
    }

    /**
     * 使用私钥对数据进行RSA解密
     *
     * @param string $cipherText 已加密的数据，使用base64编码
     * @param string $prk 私钥
     * @return string 解密后的数据
     * @throws DecryptException 如果解密失败
     */
    public static function RSADecryptByPri($cipherText, $prk)
    {
        if (!openssl_private_decrypt(base64_decode($cipherText), $plainText, $prk, OPENSSL_PKCS1_PADDING)) {
            throw new DecryptException('AESKey 解密错误');
        }

        return (string)$plainText;
    }

    /**
     * 加载X509格式的证书文件
     *
     * @param string $path 证书文件路径
     * @return array 证书详细信息
     * @throws DecryptException 如果加载或解析证书失败
     */
    public static function loadX509Cert($path)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new DecryptException('loadx509Cert::file_get_contents ERROR');
            }
            $cert = chunk_split(base64_encode($file), 64, "\n");
            $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
            $res = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);

            if (!$detail) {
                throw new DecryptException('loadX509Cert::openssl_pkey_get_details ERROR');
            }

            return $detail['key'];
        } catch (DecryptException $e) {
            throw $e;
        }
    }

    /**
     * 加载PKCS#12格式的证书文件
     *
     * @param string $path 证书文件路径
     * @param string $pwd 证书密码
     * @return mixed 证书中的私钥信息
     * @throws DecryptException 如果加载或解析证书失败
     */
    public static function loadPk12Cert($path, $pwd)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new DecryptException('loadPk12Cert::file_get_contents');
            }

            if (!openssl_pkcs12_read($file, $cert, $pwd)) {
                throw new DecryptException('loadPk12Cert::openssl_pkcs12_read ERROR');
            }
            return $cert['pkey'];
        } catch (DecryptException $e) {
            throw $e;
        }
    }

    /**
     * 使用私钥对数据进行签名
     *
     * @param string $plainText 待签名的原始数据
     * @param string $path 私钥文件路径
     * @return string 签名数据，使用base64编码
     * @throws DecryptException 如果签名失败
     */
    public static function sign($plainText, $path)
    {
        try {
            $resource = openssl_pkey_get_private($path);
            $result = openssl_sign($plainText, $sign, $resource, OPENSSL_ALGO_SHA256);
            openssl_free_key($resource);

            if (!$result) {
                throw new DecryptException('签名出错' . $plainText);
            }

            return base64_encode($sign);
        } catch (DecryptException $e) {
            throw $e;
        }
    }
}
